package com.example.permissionsystem1_1.util;

import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;

/**
 * 编译和解析二维码的工具类
 * @author Administrator
 *
 */
public class QRCodeUtil {
	/** 二维码编码字符集 */
	private static final String CHARSET="UTF-8";
	/** 文件的后缀 */
	private static final String FILE_TYPE="JPG";
	/** 二维码的尺寸(长和宽) */
	private static final Integer QRCODE_SIZE=300;
	/** 二维码中的logo的高度 */
	private static final Integer LOGO_HEIGHT=60;
	/** 二维码的logo的宽度 */
	private static final Integer LOGO_WIDTH=60;
	/** 二维码的空白区域大小(范围0~4) */
	private static final Integer MARGIN=0;
	
	/**
	 * 将指定的(字符串)内容编译生成二维码数据,并封装在(BufferedImage类型的)图片对象中并返回:相当于将指定字符串内容转成二维码(图片数据)的工具方法
	 * @param content 要转成二维码的原始内容信息
	 * @return
	 * @throws WriterException
	 */
	public static BufferedImage encodeStringToQRCodeImage(String content) throws WriterException{
		HashMap<EncodeHintType, Object> hints=new HashMap<EncodeHintType, Object>();
		//设置纠错等级
		hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
		//设置编码字符集
		hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
		//设置二维码距所在图片的边缘的空白区域大小
		hints.put(EncodeHintType.MARGIN, MARGIN);
		//将指定的原始数据根据指定的参数设置,生成对应的二维码数据(位矩阵格式的数据)
		BitMatrix bitMatrix=new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE,hints);
		//获取要生成的二维码的高度
		Integer height=bitMatrix.getHeight();
		//获取要生成的二维码的宽度
		Integer width=bitMatrix.getWidth();
		//创建一个跟要生成的二维码的高度和宽度相同的(BufferedImage类型的)图片对象:该图片对象用于封装(寄存)二维码的数据
		BufferedImage QRCodeBufferedImage=new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		
		//*将生成的二维码的数据,以像素为单位逐一存入用于封装(寄存)二维码数据的图片对象中
		for(int x=0;x<width;x++){
			for(int y=0;y<height;y++){
				QRCodeBufferedImage.setRGB(x, y, bitMatrix.get(x, y)?0xFF000000:0xFFFFFFFF);
			}
		}
		
		System.err.println("返回已经封装(寄存)了生成的二维码数据的(BufferedImage类型的)图片对象");
		//返回已经封装(寄存)了生成的二维码数据的(BufferedImage类型的)图片对象
		return QRCodeBufferedImage;
	}
	
	/**
	 * 向(BufferedImage类型的)二维码图片对象中插入指定的logo图片的工具方法:插入logo图片后将新生成的带有logo图片的(BufferedImage类型的)二维码图片对象返回
	 * @param QRCodeBufferedImage 未插入logo图片的二维码的(BufferedImage类型的)图片对象
	 * @param logoImageFilePath 要插入的logo图片的文件所在的地址
	 * @param needCompressed 是否需要压缩logo图片的开关
	 * @return
	 * @throws IOException
	 */
	public static BufferedImage insertLogoImageToQRCodeImage(BufferedImage QRCodeBufferedImage,String logoImageFilePath,Boolean needCompressed) throws IOException{
		//++参数合法性校验和处理++
		if(QRCodeBufferedImage==null||logoImageFilePath==null||"".equals(logoImageFilePath)){
			System.err.println("参数不合法,处理中断..");
			return QRCodeBufferedImage;//参数不合法时,直接原样返回即可
		}
		if(needCompressed==null){//如果needCompressed未给出,则按缺省值处理:缺省为flase,表示不进行压缩处理
			System.err.println("needCompressed未给出,则按缺省值处理:缺省为flase,表示不进行压缩处理..");
			needCompressed=false;
		}
		File logoImagefile=new File(logoImageFilePath);
		if(!logoImagefile.exists()){//如果logo图片的文件不存在,则直接原样返回即可
			System.err.println("logo图片的文件不存在,处理中断..");
			return QRCodeBufferedImage;
		}
		
		//++进行正常的处理++
		//根据给定的logo图片文件的位置,来读取指定的logo图片并封装到Image类型的图片对象中
		Image logoImage=ImageIO.read(logoImagefile);
		//获取logo图片的宽度
		Integer width=logoImage.getWidth(null);
		//获取logo图片的高度
		Integer height=logoImage.getHeight(null);
		
		//如果needCompressed为true,表示要压缩二维码中的logo图片:所以要进行压缩logo图片的处理
		if(needCompressed){
			if(width>LOGO_WIDTH){//如果logo图片的宽度比我们预定的logo宽度大,则将其缩小到我们预定的logo宽度
				width=LOGO_WIDTH;
			}
			if(height>LOGO_HEIGHT){//如果logo图片的高度比我们预定的logo高度大,则将其缩小到我们预定的logo高度
				height=LOGO_HEIGHT;
			}
			
			//根据原始的logo图片的(Image类型的)图片对象的参数,来创建一个用于接收压缩处理后的logo图片的(Image类型的)图片对象
			Image CompressedLogoImage=logoImage.getScaledInstance(width, height, Image.SCALE_SMOOTH);
			//创建用于封装压缩处理后的logo图片的(Image类型的)图片对象的(BufferedImage类型的)图片对象
			BufferedImage CompressedLogoBufferedImage=new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
			//获取进行图片内容绘制的"画笔"对象
			Graphics g=CompressedLogoBufferedImage.getGraphics();
			//用画笔对象向用于封装(寄存)压缩处理后的logo图片的(Image类型的)图片对象写入logo图片的全部数据
			g.drawImage(CompressedLogoImage, 0, 0, null);
			//让画笔对象释放占用的资源
			g.dispose();
			//将封装(寄存)压缩处理后的logo图片的(Image类型的)图片对象替代原始的未被压缩处理的封装(寄存)logo图片的(Image类型的)图片对象
			logoImage=CompressedLogoImage;
		}
		
		//获取二维码的图片对象的2D画笔对象(Graphics2D)
		Graphics2D g2D=QRCodeBufferedImage.createGraphics();
		
		//用于设置二维码图片的中间添加了指定宽高的logo图片后的二维码的图片情况
		Integer x=(QRCODE_SIZE-width)/2;
		Integer y=(QRCODE_SIZE-height)/2;
		
		//重新绘制添加了指定宽高的logo图片后的二维码的图片效果
		g2D.drawImage(logoImage, x, y, width, height, null);
		Shape shape=new RoundRectangle2D.Float(x, y, width, height, 6, 6);
		g2D.setStroke(new BasicStroke(3f));
		g2D.draw(shape);
		
		//让画笔对象释放占用的资源
		g2D.dispose();
		
		System.err.println("返回已经添加上logo图片后的(BufferedImage类型的)二维码的图片对象");
		//返回已经添加上logo图片后的(BufferedImage类型的)二维码的图片对象
		return QRCodeBufferedImage;
	}
	
	/**
	 * 将给定的(BufferedImage类型的)二维码图片对象按指定的文件路径(在电脑存盘中)生成相应的文件对象
	 * @param QRCodeBufferedImage 封装二维码数据的(BufferedImage类型的)图片对象
	 * @param QRCodeFilePath 生成的文件对象的地址路径
	 * @return
	 * @throws IOException
	 */
	public static boolean makeQRCodeBufferedImageToQRCodeFile(BufferedImage QRCodeBufferedImage,String QRCodeFilePath) throws IOException{
		//如果未给定的二维码图片的文件存储路径,则按缺省值处理即可
		if(QRCodeFilePath==null||"".equals(QRCodeFilePath)){
			QRCodeFilePath="D:\\QRCodeFileDemo.jpg";//缺省放在D盘下的指定名称的文件中
		}
		
		//创建一个映射指定文件路径的文件对象
		File file=new File(QRCodeFilePath);
		if(!file.isDirectory()){//如果文件对象所映射的不是一个已经存在的文件夹,则直接新建一个指定名称的文件
			file.mkdir();//新建一个指定名称的文件
		}else{//如果文件对象所映射的是一个已经存在的文件夹,则直接返回代表失败的结果值
			System.err.println("文件对象所映射的是一个已经存在的文件夹,创建二维码图片的文件失败");
			return false;
		}
		
		//将二维码图片对象的数据写入到新创建的指定的文件里
		boolean result=ImageIO.write(QRCodeBufferedImage, FILE_TYPE, new File(QRCodeFilePath));
		System.err.println("将二维码图片对象的数据写入到新创建的指定的文件里:"+result);
		return result;//返回二维码图片文件的生成结果
	}
	
	/**
	 * 将二维码(数据)解析成直观可读的字符串类型的信息数据
	 * @param obj
	 * @return
	 * @throws IOException
	 * @throws NotFoundException
	 */
	public static String decodeQRCodeToString(Object obj) throws IOException, NotFoundException{
		//用于封装二维码数据的(BufferedImage类型的)图片对象
		@SuppressWarnings("unused")
		BufferedImage QRCodeBufferedImage=null;
		if(obj instanceof String){//如果传入的是字符串类型的参数,则按二维码图片文件的地址路径来处理
			String QECodeFilePath=(String)obj;
			File QRCodeFile=new File(QECodeFilePath);
			QRCodeBufferedImage=ImageIO.read(QRCodeFile);
		}else if(obj instanceof File){//如果传入的是文件类型的参数,则按二维码文件对象来处理
			File QRCodeFile=(File)obj;
			QRCodeBufferedImage=ImageIO.read(QRCodeFile);
		}else if(obj instanceof BufferedImage){//如果传入的是BufferedImage类型的图片对象,则直接赋值即可
			QRCodeBufferedImage=(BufferedImage)obj;
		}else{//其他类型的参数都是错误的,直接返回null
			System.err.println("其他类型的参数都是错误的,直接返回null");
			return null;
		}
		
		if(QRCodeBufferedImage==null){//如果BufferedImage类型的图片对象生成失败,则直接返回null
			System.err.println("BufferedImage类型的图片对象生成失败,则直接返回null");
			return null;
		}
		
		BufferedImageLuminanceSource bufferedImageLuminanceSource=new BufferedImageLuminanceSource(QRCodeBufferedImage);
		BinaryBitmap binaryBitmap=new BinaryBitmap(new HybridBinarizer(bufferedImageLuminanceSource));
		HashMap<DecodeHintType,Object> hints=new HashMap<DecodeHintType, Object>();
		//设置解析二维码数据的字符集
		hints.put(DecodeHintType.CHARACTER_SET, CHARSET);
		//对二维码数据进行解析,从而生成(Result类型)的信息数据
		Result result=new MultiFormatReader().decode(binaryBitmap, hints);
		//将二维码数据解析后的Result类型的信息数据,转成(直观可读的)字符串类型的信息数据
		String resultString=result.getText();
		
		//将二维码数据解析后转成的(直观可读的)字符串类型的信息数据返回
		return resultString;
	}
	
	
	
}
